﻿/*!
 * @file normalizedrgb.cpp
 * @author 橋本敦史
 * @date Last Change:2010/Oct/22.
 */
#include "ImageBackgroundSubtractorNormalizedRGB.h"

using namespace std;

using namespace mmpl;
using namespace mmpl::image;
/*!
 * @brief デフォルトコンストラクタ
 */
BackgroundSubtractorNormalizedRGB::BackgroundSubtractorNormalizedRGB(int threshold,int threBlack,int max_luminance_diff):threshold(threshold),threBlack(threBlack),max_luminance_diff(max_luminance_diff){
}

/*!
 * @brief デストラクタ
 */
BackgroundSubtractorNormalizedRGB::~BackgroundSubtractorNormalizedRGB(){

}

/*!
 * @brief 背景画像を登録する
 * */
void BackgroundSubtractorNormalizedRGB::setBGImage(const Image& img){
	bgimg = img;
	diffimg = img;
}

/*!
 * @brief 背景差分画像を作成する
 * */
void BackgroundSubtractorNormalizedRGB::apply(const Image& img, BGSubtractedImage* oimg){
	if(bgimg.getWidth()!=img.getWidth()){
		std::cerr << "at " << __FILE__ << ": " << __LINE__ << std::endl;
		cerr << "Warning: 登録された背景画像と入力画像の幅が違います"<<endl;
		return;
	}
	else if(bgimg.getHeight()!=img.getHeight()){
		std::cerr << "at " << __FILE__ << ": " << __LINE__ << std::endl;
		cerr << "Warning: 登録された背景画像と入力画像の高さが違います" << endl;
		return;
	}
	else if(bgimg.getDepth()!=img.getDepth()){
		std::cerr << "at " << __FILE__ << ": " << __LINE__ << std::endl;
		cerr << "Warning: 登録された背景画像と入力画像のDepthが違います" << endl;
		return;
	}
	else if(bgimg.getChannels()!=img.getChannels()){
		std::cerr << "at " << __FILE__ << ": " << __LINE__ << std::endl;
		cerr << "Warning: 登録された背景画像と入力画像のチャンネル数が違います" << endl;
		return;
	}

	if(oimg->getWidth()!=img.getWidth() || oimg->getWidth()!=img.getHeight()||	oimg->getDepth()!=img.getDepth() || oimg->getChannels()!=img.getChannels()){
		oimg->setWidth(img.getWidth());
		oimg->setHeight(img.getHeight());
		oimg->setDepth(img.getDepth());
		oimg->setChannels(img.getChannels());
	}
	Color col,bgcol,diff;
	Color black(0,0,0);
	Color white(255,255,255);
	for(int y=0;y<img.getHeight();y++){
		for(int x=0;x<img.getWidth();x++){
			col = img.getColor(x,y);
			bgcol = bgimg.getColor(x,y);
			diff = col.absDiff(bgcol);
			//! しきい値により、確実に背景であるところを抑える
			if(diff[0]<=threshold && diff[1]<=threshold && diff[2]<=threshold){
				oimg->setColor(x,y,oimg->getBGColor());
				// 赤は普通に背景とされた領域
				diffimg.setColor(x,y,Color(0,0,255));
				continue;
			}

			//! 一ヶ所でも、しきい値以上に明るい領域があれば影ではないとみなす
			/*			diff -= bgcol-col;
						if(diff[0]>threshold || diff[1]>threshold || diff[2]>threshold){
			//影ではないと思って処理から外す
			oimg->setColor(x,y,col);
			diffimg.setColor(x,y,Color(255,0,0));
			continue;
			}
			*/
			//! 残ったところは影かどうか怪しい
			//! 暗すぎる部分は前景物体として処理
			if(col[0]<threBlack && col[1]<threBlack && col[2] < threBlack){
				//! 影にしては暗すぎると判断する
				oimg->setColor(x,y,img.getColor(x,y));
				// ピンクは影にしては暗すぎるため、前景とされた領域
				diffimg.setColor(x,y,Color(127,127,255));
				continue;
			}


			//! 取り合えず、正規化色でチェックしてみる
			MmplVector temp;
			temp.resize(3);
			temp[0]=col[0];
			temp[1]=col[1];
			temp[2]=col[2];

			int bg_luminance = bgcol[0]>bgcol[1]?bgcol[0]:bgcol[1];
			bg_luminance = bg_luminance>bgcol[2]?bg_luminance:bgcol[2];
			//cout << "L_bg:L_fg = " << bg_luminance << ":" << temp.max() << endl;
			if(abs(bg_luminance-temp.max())>max_luminance_diff){
				// 紫は元の色に対して輝度変化が大きすぎるため、前景とされた領域
				diffimg.setColor(x,y,Color(255,0,255));
				oimg->setColor(x,y,img.getColor(x,y));
				continue;
			}

			temp.regularize(1.0);
			int sum = bgcol[0]+bgcol[1]+bgcol[2];


			temp[0]*=sum;
			temp[1]*=sum;
			temp[2]*=sum;

			col[0] = temp[0] < 255 ? static_cast<unsigned char>(temp[0]) : 255;
			col[1] = temp[1] < 255 ? static_cast<unsigned char>(temp[1]) : 255;
			col[2] = temp[2] < 255 ? static_cast<unsigned char>(temp[2]) : 255;
			diff = col.absDiff(bgcol);
			//! 正規化RGB色で再評価する
			if(diff[0]<=threshold && diff[1]<=threshold && diff[2]<=threshold){
				oimg->setColor(x,y,oimg->getBGColor());
				// 緑は正規化RGBが一致して背景になった領域
				diffimg.setColor(x,y,Color(0,255,0));
				continue;
			}
			oimg->setColor(x,y,img.getColor(x,y));
			// 灰色は正規化RGBが一致せず、前景になった領域
			diffimg.setColor(x,y,Color(127,127,127));
		}
	}
//	cvShowImage("debug",diffimg.getIplImage());	
}
/**** Accessor ****/
void BackgroundSubtractorNormalizedRGB::setThreshold(int threshold){
	this->threshold = threshold;
}

int BackgroundSubtractorNormalizedRGB::getThreshold()const{
	return threshold;
}
